package cn.xqapp.core;

import cn.xqapp.DiscardServer.DiscardServer;
import cn.xqapp.EchoServer.EchoServer;
import cn.xqapp.GatewayClient.GatewayClient;
import cn.xqapp.GatewayServer.GatewayServer;
import cn.xqapp.TimeClient.TimeClient;
import cn.xqapp.TimeServer.TimeServer;
import org.apache.commons.cli.*;

import java.util.HashMap;
import java.util.Map;
import java.util.function.Supplier;

class DefaultOptions {
    private String host;

    public String getHost() {
        return host;
    }

    public void setHost(String host) {
        this.host = host;
    }

    private int port;

    public int getPort() {
        return port;
    }

    public void setPort(int port) {
        this.port = port;
    }

    private String server;

    public String getServer() {
        return server;
    }

    public void setServer(String server) {
        this.server = server;
    }

    private int clientNumber;

    public int getClientNumber() {
        return clientNumber;
    }

    public void setClientNumber(int clientNumber) {
        this.clientNumber = clientNumber;
    }

    private int messageSize;

    public int getMessageSize() {
        return messageSize;
    }

    public void setMessageSize(int messageSize) {
        this.messageSize = messageSize;
    }

    private int messageInterval;

    public int getMessageInterval() {
        return messageInterval;
    }

    public void setMessageInterval(int messageInterval) {
        this.messageInterval = messageInterval;
    }
}

public class Main {

    public static void main(String[] args) {

        Options options = new Options();
        options.addOption("app", "application", true, "application to run");
        options.addOption("h", "host", true, "host address to connect");
        options.addOption("p", "port", true, "server port to bind");
        options.addOption("cnum", "client_num", true, "client number");
        options.addOption("msize", "message_size", true, "message size");
        options.addOption("mintvl", "message_interval", true, "message interval");

        CommandLineParser parser = new DefaultParser();

        DefaultOptions defaultOptions = new DefaultOptions();
        defaultOptions.setServer("DiscardServer");
        defaultOptions.setHost("localhost");
        defaultOptions.setPort(80);
        defaultOptions.setClientNumber(1);
        defaultOptions.setMessageSize(1024);
        defaultOptions.setMessageInterval(0);

        try {
            // parse the options
            CommandLine cli = parser.parse(options, args);
            if(cli.hasOption("app")) defaultOptions.setServer(cli.getOptionValue("app"));
            if(cli.hasOption("h")) defaultOptions.setHost(cli.getOptionValue("h"));
            if(cli.hasOption("p")) defaultOptions.setPort(Integer.parseInt(cli.getOptionValue("p")));
            if(cli.hasOption("cnum")) defaultOptions.setClientNumber(Integer.parseInt(cli.getOptionValue("cnum")));
            if(cli.hasOption("msize")) defaultOptions.setMessageSize(Integer.parseInt(cli.getOptionValue("msize")));
            if(cli.hasOption("mintvl")) defaultOptions.setMessageInterval(Integer.parseInt(cli.getOptionValue("mintvl")));

            Map<String, Supplier<Runnable>> runnableMap = new HashMap<>();
            runnableMap.put("DiscardServer", ()-> new DiscardServer(defaultOptions.getPort()));
            runnableMap.put("EchoServer", ()->new EchoServer(defaultOptions.getPort()));

            runnableMap.put("TimeServer", ()->new TimeServer(defaultOptions.getPort()));
            runnableMap.put("TimeClient", ()->new TimeClient(defaultOptions.getHost(), defaultOptions.getPort()));

            runnableMap.put("GatewayServer", ()->new GatewayServer(defaultOptions.getPort()));
            runnableMap.put("GatewayClient", ()->new GatewayClient(
                    defaultOptions.getHost(), defaultOptions.getPort(),
                    defaultOptions.getClientNumber(), defaultOptions.getMessageSize(), defaultOptions.getMessageInterval()));

            Supplier<Runnable> targetServerSupplier = runnableMap.get(defaultOptions.getServer());
            assert(targetServerSupplier != null);

            if(targetServerSupplier != null){
                System.out.println(String.format("%s running on port %d ...", defaultOptions.getServer(), defaultOptions.getPort()));
                targetServerSupplier.get().run();
            }
        } catch (Exception e) {
            System.out.println("Unexpected exception:" + e.getMessage());
        }
    }
}
